﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows.Forms;
using Axiom.Core;
using System.Xml;
using System.Text.RegularExpressions;
using System.IO;
using System.ComponentModel;

namespace GameDesigner
{
    public partial class StartUpWizard : Form, IDisposable
    {
        Root AxiomEngine = Program.AxiomEngine;
        public string EngineDescriptorFile { get; private set; }
        ResourceList resourceList;
        object WorldGeometryConfig;

        #region Wizard List Manipulation
        delegate bool validateForm();
        List<validateForm> WizardValidations;
        List<Panel> WizardSteps;
        int wizardPage = 0;

        bool wzStepEngingeProjectConfigValidate()
        {
            if (
                String.IsNullOrEmpty(txbProjectName.Text) ||
                String.IsNullOrEmpty(odProjectAdress.Text)
                )
                return false;

            return true;
        }

        bool wzStepCoreSetupValidate()
        {
            if (
                cmbRenderSystem.SelectedIndex < 0 ||
                cmbScenemanagerType.SelectedIndex < 0
                )
                return false;

            resourceList.RootFolder = new DirectoryInfo(odProjectAdress.Text + Path.DirectorySeparatorChar + Descriptor.ResourceMainFolder);
            return true;
        }

        bool wzStepResourcesValidate()
        {
            return true;
        }

        private void initializeWizardSteps()
        {
            WizardSteps = new List<Panel>();
            WizardValidations = new List<validateForm>();

            WizardSteps.Add(wzStepEngingeProjectConfig);
            WizardValidations.Add(wzStepEngingeProjectConfigValidate);

            WizardSteps.Add(wzStepCoreSetup);
            WizardValidations.Add(wzStepCoreSetupValidate);

            WizardSteps.Add(wzStepResources);
            WizardValidations.Add(wzStepResourcesValidate);

            WizardSteps[wizardPage].BringToFront();
            checkBottuns();
        }
        #endregion

        public StartUpWizard()
        {
            InitializeComponent();

            initializeWizardSteps();

            resourceList = new ResourceList(new List<Resource>(), null);
            ResourceEditor.SelectedObject = resourceList;
        }

        private void cmbRenderSystem_DropDown(object sender, EventArgs e)
        {
            ((ComboBox)sender).Items.Clear();

            foreach (string key in AxiomEngine.RenderSystems.Keys)
                ((ComboBox)sender).Items.Add(key);
        }

        private void cmbScenemanagerName_DropDown(object sender, EventArgs e)
        {
            ((ComboBox)sender).Items.Clear();
            ((ComboBox)sender).Items.Add("Auto detect");

            for (int i = 0; i < AxiomEngine.MetaDataList.Count; i++)
            {
                if (AxiomEngine.MetaDataList[i].sceneTypeMask.Equals(
                        (SceneType)cmbScenemanagerType.SelectedItem))
                {
                    ((ComboBox)sender).Items.Add(new KeyValuePair<int, string>
                        (i, AxiomEngine.MetaDataList[i].typeName)
                        );
                }
            }

        }

        private void cmbScenemanagerType_DropDown(object sender, EventArgs e)
        {
            ((ComboBox)sender).Items.Clear();

            foreach (int snmTyp in Enum.GetValues(typeof(SceneType)))
                ((ComboBox)sender).Items.Add((SceneType)snmTyp);

        }

        private void cmbScenemanagerType_SelectedIndexChanged(object sender, EventArgs e)
        {
            cmbScenemanagerName.SelectedIndex = -1;
            cmbScenemanagerName.Enabled = ((ComboBox)sender).SelectedIndex > -1 ? true : false;
        }

        private void cmbScenemanagerName_SelectedIndexChanged(object sender, EventArgs e)
        {
            ComboBox cmb = (ComboBox)sender;
            if (cmb.SelectedIndex > 0)
            {
                WorlGeometryChb.Enabled =
                EditWorldGeometryBtn.Enabled =
                    AxiomEngine.MetaDataList[((KeyValuePair<int, string>)cmb.SelectedItem).Key].worldGeometrySupported;

                lblScenemanagerDescription.Text =
                    AxiomEngine.MetaDataList[((KeyValuePair<int, string>)cmb.SelectedItem).Key].description;
            }
            else if (cmb.SelectedIndex == 0)
            {
                WorlGeometryChb.Enabled =
                EditWorldGeometryBtn.Enabled =
                    false;

                lblScenemanagerDescription.Text = "Be carefull using this option, falling back to generic may change every thing.";
            }
            else
            {
                WorlGeometryChb.Enabled =
                EditWorldGeometryBtn.Enabled =
                    false;

                lblScenemanagerDescription.Text = "";
            }
        }

        private void btnNext_Click(object sender, EventArgs e)
        {
            if (!WizardValidations[wizardPage]())
            {
                MessageBox.Show("There are some needed data left empty!\nPlease fill all starred items before leaving this page.", "Error");
                return;
            }

            if (wizardPage < WizardSteps.Count)
            {
                wizardPage++;
                WizardSteps[wizardPage].BringToFront();
                checkBottuns();
            }
        }

        private void btnBack_Click(object sender, EventArgs e)
        {
            if (wizardPage > 0)
            {
                wizardPage--;
                WizardSteps[wizardPage].BringToFront();
                checkBottuns();
            }

        }

        private void checkBottuns()
        {
            if (wizardPage <= 0)
                btnBack.Enabled = false;
            else
                btnBack.Enabled = true;

            if (wizardPage >= WizardSteps.Count - 1)
            {
                btnNext.Visible = false;
                btnFinish.Visible = true;
            }
            else
            {
                btnNext.Visible = true;
                btnFinish.Visible = false;
            }
        }

        private void btnFinish_Click(object sender, EventArgs e)
        {
            if (!WizardValidations[wizardPage]())
            {
                MessageBox.Show("There are some needed data left empty!\nPlease fill all starred items before leaving this page.", "Error");
                return;
            }

            EngineDescriptorFile = odProjectAdress.Text + System.IO.Path.DirectorySeparatorChar + MakeValidFileName(txbProjectName.Text) + ".xml";

            //create a descriptor object and use it to save the xml
            Descriptor descriptor = new Descriptor();

            //write render system
            descriptor.RenderSystemKey = cmbRenderSystem.SelectedItem.ToString();

            //write scene manager
            descriptor.SceneType = (SceneType)cmbScenemanagerType.SelectedItem;
            if (cmbScenemanagerName.SelectedIndex > 0)
                descriptor.SceneTypeName = ((KeyValuePair<int, string>)cmbScenemanagerName.SelectedItem).Value;

            //write terrain
            if (WorlGeometryChb.Enabled && WorlGeometryChb.Checked)
                descriptor.Terrain = WriteWorldGeometryConfigFile();

            //write resources
            descriptor.ResourcesList = resourceList.Resources;
            descriptor.DefaultMipmapCount = resourceList.DefaultMipMapCount;

            descriptor.SaveToFile(EngineDescriptorFile, 
                AxiomEngine.RenderSystems[cmbRenderSystem.SelectedItem.ToString()].ConfigOptions);

            MessageBox.Show("Saving project done successfully.Press OK to edit this project in GameEditor.", "Info", MessageBoxButtons.OK);

            //go for editing
            this.DialogResult = DialogResult.OK;
        }

        /// <summary>
        /// World Geometry options has to be saved to a seperate xml file inside resources. 
        /// terrain node contains it's name only
        /// </summary>
        /// <param name="engineDescriptor"></param>
        private string WriteWorldGeometryConfigFile()
        {
            //every world geometry has its own writer
            if (WorldGeometryConfig is TerrainSceneManagerTerrainConfig)
            {
                TerrainSceneManagerTerrainConfig terCfg = (TerrainSceneManagerTerrainConfig)WorldGeometryConfig;

                System.Diagnostics.Debug.Write("Adding terrain configs to XML engine descriptor.");

                //add terrian files to resources if not already are
                //we find all file properties from this object and localize them
                foreach (PropertyDescriptor fileProp in TerrainSceneManagerTerrainConfig.GetFileProperties())
                {
                    string fileName = (string)fileProp.GetValue(terCfg);
                    if (!String.IsNullOrEmpty(fileName) && !resourceList.IsFileInResources(fileName))
                    {
                        resourceList.AddFileToGeneral(fileName);
                    }
                }

                terCfg.SaveToXml(resourceList.GetGeneralResource().Location + 
                    Path.DirectorySeparatorChar + Descriptor.TerrainDescriptorFileName);

                return Descriptor.TerrainDescriptorFileName;
            }
            else
                return null;

        }

        private static string MakeValidFileName(string name)
        {
            string invalidChars = Regex.Escape(new string(Path.GetInvalidFileNameChars()));
            string invalidReStr = string.Format(@"[{0}]", invalidChars);
            return Regex.Replace(name, invalidReStr, "_");
        }

        #region IDisposable Members

        void IDisposable.Dispose()
        {
            AxiomEngine.QueueEndRendering();
            //AxiomEngine.Dispose();
        }

        #endregion

        private void cmbRenderSystem_SelectedIndexChanged(object sender, EventArgs e)
        {
            if (((ComboBox)sender).SelectedIndex >= 0)
                RenderSystemPropertiesBtn.Enabled = true;
            else
                RenderSystemPropertiesBtn.Enabled = false;

        }

        private void RenderSystemPropertiesBtn_Click(object sender, EventArgs e)
        {
            ConfigOptionCollectionEditorForm eFrm = new ConfigOptionCollectionEditorForm(
                AxiomEngine.RenderSystems[cmbRenderSystem.SelectedItem.ToString()].ConfigOptions);

            eFrm.ShowDialog();

        }

        private void EditWorldGeometryBtn_Click(object sender, EventArgs e)
        {
            switch (((KeyValuePair<int, string>)cmbScenemanagerName.SelectedItem).Value)
            {
                case "TerrainSceneManager":
                    if(
                        WorldGeometryConfig == null ||
                        !(WorldGeometryConfig is TerrainSceneManagerTerrainConfig)
                        )
                    WorldGeometryConfig = new TerrainSceneManagerTerrainConfig(resourceList);
                    break;

                default:
                    MessageBox.Show("The config type of World Geometry for this SceneManager is unknown.");
                    return;
            }

            SinglePropertyEidtorForm edt = new SinglePropertyEidtorForm();
            edt.EditorPG.SelectedObject = WorldGeometryConfig;
            if (edt.ShowDialog() == DialogResult.OK)
            {
                if (!WorlGeometryChb.Checked)
                    WorlGeometryChb.Checked = true;
            }

        }

    }
}
